{* this unit mainly talk to DataAccessor
   HadeVisitor->dataAccessor->ObjectFactory
   dataAccessor responsible for Executing/open Query
   ObjectFactory responsible for serialize/deserialize Record <-> Object *}
unit hadesqlvisitor;

{$mode objfpc}{$H+}

interface

uses
  Classes,
  SysUtils,
  hadebasevisitor,
  hadedataaccessor,
  hadeconnectionmanager,
  hademapbase,
  hademapping,
  hadecriteria,
  hadeobject;

type

  { THadeSQLAbs }

  THadeSQLAbs = class(THadeVisitor)
  protected
    FAccessor: THadeDataAccessor;
    fVisited: THadeVisited;
    FMappingManager: THadeMappingManager;
  protected

  public
    constructor Create(const AConnection: THadeConnection); override;
    procedure Execute(const Instance: IHadeVisited); override;
    destructor Destroy; override;
  end;

  { THadeSQLSaveAbs }

  THadeSQLSaveAbs = class(THadeSQLAbs)
  protected
    procedure save(); virtual;

    { function to Convert WriteAble Property Name and Value of THadeObject
      to THadeSimplemap , so dataAccessor can process it with easy }
    function getWriteAbleSimpleMap(const AObject: THadeVisited;
      const AMapClass: THadeMapClass): THadeMapSimpleValue;
  public
    procedure Execute(const Instance: IHadeVisited); override;
  end;

  { THadeSQLCreate }

  THadeSQLCreate = class(THadeSQLSaveAbs)
  protected
    function Accept: boolean; override;
    procedure save(); override;
  end;

  { THadeSQLUpdate }

  THadeSQLUpdate = class(THadeSQLSaveAbs)
  protected
    function Accept: boolean; override;
    procedure save(); override;
  end;

  { THadeSQLDelete }

  THadeSQLDelete = class(THadeSQLSaveAbs)
  protected
    function Accept: boolean; override;
    procedure save(); override;
  end;

  { THadeSQLReadAbs }

  THadeSQLReadAbs = class(THadeSQLAbs)
  protected
    procedure Read(const ACriteria: THadeCriteria; const APropsOrder: array of string;
      const AOrderType: THadeOrderType; const limit: integer;
      const offset: integer); virtual;

    procedure FillOrderBy(const APropsOrder: array of string;
      var AorderList: TStringList);
  public
    procedure Execute(const Instance: IHadeVisited; const ACriteria: THadeCriteria;
      const APropsOrder: array of string; const AOrderType: THadeOrderType = oASC;
      const limit: integer = 0; const offset: integer = 0); override;
  end;

  THadeSQLRead = class(THadeSQLReadAbs)
  protected
    function Accept: boolean; override;
    procedure Read(const ACriteria: THadeCriteria; const APropsOrder: array of string;
      const AOrderType: THadeOrderType; const limit: integer;
      const offset: integer); override;
  end;

  { THadeSQLSaveList }
  {* Create Operation for THadeObjectList *}
  THadeSQLSaveList = class(THadeSQLSaveAbs)
  protected
    function Accept: boolean; override;
    procedure save(); override;
  public
    procedure Execute(const Instance: IHadeVisited); override;
  end;

  { THadeSQLReadList }

  THadeSQLReadList = class(THadeSQLReadAbs)
  protected
    procedure Read(const ACriteria: THadeCriteria; const APropsOrder: array of string;
      const AOrderType: THadeOrderType; const limit: integer;
      const offset: integer); override;

    function Accept: boolean; override;
  public
    procedure Execute(const Instance: IHadeVisited; const ACriteria: THadeCriteria;
      const APropsOrder: array of string; const AOrderType: THadeOrderType = oASC;
      const limit: integer = 0; const offset: integer = 0); override;
  end;


implementation

uses
  hadevisitormanager,
  hadertti,
  typinfo;

{ THadeSQLSaveList }

function THadeSQLSaveList.Accept: boolean;
begin
  Result := fVisited is THadeObjectList;
end;

procedure THadeSQLSaveList.save;
var
  ACols: THadeMapSimpleValue;
  tablename: string;
  eleName: string;
  iloop: integer;
  whereClause: string;
  mapclass: THadeMapClass;
  objList: THadeObjectList;
  obj: THadeObject;
  ASQL: TStringList;
begin
  ASQL := TStringList.Create;
  objList := THadeObjectList(fVisited);
  try
    eleName := FMappingManager.RegObjecByName(FVisited.ClassName).ClassBOName;
    mapclass := FMappingManager.ClassByName(eleName);
    tablename := mapclass.Table.TableName;

    for iloop := 0 to pred(objlist.Count) do
    begin
      obj := objList.items[iloop];
      case obj.objectState of

        posUpdate:
        begin
          ACols := getWriteAbleSimpleMap(obj, MapClass);
          whereClause := MapClass.Table.getPK.ColumnName + ' = ' +
            obj.OID.StringOID;
          fAccessor.Update(tableName, obj, Acols, whereClause, False);
          //False prevent execute
        end;

        posCreate:
        begin
          ACols := getWriteAbleSimpleMap(obj, MapClass);
          fAccessor.Insert(tableName, obj, ACols, False);
        end;

        posDelete:
        begin
          whereClause := MapClass.Table.getPK.ColumnName + ' = ' +
            obj.OID.StringOID;
          fAccessor.Delete(tableName, whereClause, obj, False);
        end;

        else
          continue;

      end;//end of case
      FreeAndNil(ACols);
      ASQL.add(fAccessor.getDataSet.SQL.Text + ';');
    end;//end iloop
    fAccessor.ExecuteMultiple(ASQL.Text);
  finally
    FreeAndNil(ASQL);
  end;
end;

procedure THadeSQLSaveList.Execute(const Instance: IHadeVisited);
begin
  fAccessor.WithoutParams := True;
  inherited Execute(Instance);
end;

{ THadeSQLReadList }

procedure THadeSQLReadList.Read(const ACriteria: THadeCriteria;
  const APropsOrder: array of string; const AOrderType: THadeOrderType;
  const limit: integer; const offset: integer);
var
  listcol, listproporder: TStringList;
  tablename: string;
  eleName: string;
  mapclass: THadeMapClass;
  iloop: integer;
  objList: THadeObjectList;
  whereclause: string;
begin
  eleName := FMappingManager.RegObjecByName(FVisited.ClassName).ClassBOName;
  mapclass := FMappingManager.ClassByName(eleName);
  tablename := mapclass.Table.TableName;
  if Assigned(ACriteria) then
    whereclause := ACriteria.getClause()
  else
    whereClause := '';
  objList := THadeObjectList(fVisited);

  fillOrderBy(APropsOrder, listPropOrder);
  listcol := MapClass.getReadableColumn;
  try
    fAccessor.Read(TableName, objList, listcol,
      listPropOrder, whereclause, AOrderType, limit, offset);
  finally
    FreeAndNil(listCol);
    FreeAndNil(listPropOrder);
    if Assigned(ACriteria) then
      if Acriteria.AutoFreeAfterUse then
        ACriteria.Free;
  end;
end;

function THadeSQLReadList.Accept: boolean;
begin
  Result := FVisited is THadeObjectList;
end;

procedure THadeSQLReadList.Execute(const Instance: IHadeVisited;
  const ACriteria: THadeCriteria; const APropsOrder: array of string;
  const AOrderType: THadeOrderType; const limit: integer; const offset: integer);
begin
  fVisited := instance as THadeVisited;
  if not Accept then
    exit;
  Read(Acriteria, APropsOrder, AOrderType, limit, offset);
end;

{ THadeSQLRead }

function THadeSQLRead.Accept: boolean;
begin
  Result := (not (FVisited is THadeObjectList));
end;

procedure THadeSQLRead.Read(const ACriteria: THadeCriteria;
  const APropsOrder: array of string; const AOrderType: THadeOrderType;
  const limit: integer; const offset: integer);
var
  MapClass: THadeMapClass;
  whereClause: string;
  listcol: TStringList;
  listOrder: TStringList;
begin
  MapClass := FMappingManager.ClassByName(FVisited.ClassName);
  if ACriteria = nil then
    whereClause := MapClass.Table.getPK.ColumnName + ' = ' + THadeObject(
      fVisited).OID.getGUID
  else
    whereClause := ACriteria.getClause();

  FillOrderBy(APropsOrder, listOrder);
  listcol := MapClass.getReadableColumn;
  try
    fAccessor.Read(MapClass.Table.TableName, THadeObject(fVisited), listcol,
      listOrder, whereClause, AOrderType, 1, 0);
  finally
    FreeAndNil(listCol);
    FreeAndNil(ListOrder);
    if Assigned(ACriteria) then
      if Acriteria.AutoFreeAfterUse then
        ACriteria.Free;
  end;
end;

{ THadeSQLReadAbs }

procedure THadeSQLReadAbs.Read(const ACriteria: THadeCriteria;
  const APropsOrder: array of string; const AOrderType: THadeOrderType;
  const limit: integer; const offset: integer);
begin

end;

procedure THadeSQLReadAbs.FillOrderBy(const APropsOrder: array of string;
  var AorderList: TStringList);
var
  iloop: integer;
begin
  AOrderList := TStringList.Create;
  for iloop := 0 to HIGH(APropsOrder) do
  begin
    AOrderList.Add(APropsOrder[iloop]);
  end;
end;

procedure THadeSQLReadAbs.Execute(const Instance: IHadeVisited;
  const ACriteria: THadeCriteria; const APropsOrder: array of string;
  const AOrderType: THadeOrderType; const limit: integer; const offset: integer);
begin
  inherited Execute(Instance);
  if (not Accept) then
    Exit;

  Read(ACriteria, APropsOrder, AOrderType, limit, offset);
end;

{ THadeSQLDelete }

function THadeSQLDelete.Accept: boolean;
begin
  Result := not (FVisited is THadeObjectList);
end;

procedure THadeSQLDelete.save;
var
  MapClass: THadeMapClass;
  whereClause: string;
begin
  MapClass := FMappingManager.ClassByName(FVisited.ClassName);
  whereClause := MapClass.Table.getPK.ColumnName + ' = ' + THadeObject(
    fVisited).OID.getGUID;

  fAccessor.Delete(MapClass.Table.TableName, whereclause, THadeObject(fVisited));
end;

{ THadeSQLUpdate }

function THadeSQLUpdate.Accept: boolean;
begin
  Result := not (FVisited is THadeObjectList);
end;

procedure THadeSQLUpdate.save;
var
  MapClass: THadeMapClass;
  whereClause: string;
  ACols: THadeMapSimpleValue;
begin
  try
    MapClass := FMappingManager.ClassByName(FVisited.ClassName);
    whereClause := MapClass.Table.getPK.ColumnName + ' = ' + THadeObject(
      fVisited).OID.StringOID;
    ACols := getWriteAbleSimpleMap(fVisited, MapClass);

    FAccessor.Update(MapClass.Table.TableName, THadeObject(fVisited),
      ACols, whereClause);
  finally
    FreeAndNil(ACols);
  end;
end;

{ THadeSQLSaveAbs }

procedure THadeSQLSaveAbs.save;
begin

end;

function THadeSQLSaveAbs.getWriteAbleSimpleMap(const AObject: THadeVisited;
  const AMapClass: THadeMapClass): THadeMapSimpleValue;
var
  simplemap: THadeMapSimpleValue;
  ColList: TStringList;
  iloop: integer;
  Value: variant;
  tmpval: THadeObject;

begin
  simplemap := THadeMapSimpleValue.Create();
  ColList := AMapClass.getWriteableColumn;
  try
    for iloop := 0 to pred(ColList.Count) do
    begin
      if ptkFK in AMapClass.PropByName(AMapClass.PropNameByCol(collist[iloop])).Column.ColInfo then
      begin
        tmpVal := THadeObject(hadertti.getHdObjectProp(
          AObject, AMapClass.PropNameByCol(collist[iloop])));
        if tmpVal = nil then
          Value := NULL
        else
          Value := tmpVal.OID.getValue;
      end
      else
        Value := getVarProp(AObject, AMapClass.PropNameByCol(colList[iloop]));
      simplemap.add(ColList[iloop], THadeSimpleValue.Create(Value));
    end;
    Result := simplemap;
  finally
    FreeAndNil(ColList);
  end;
end;

procedure THadeSQLSaveAbs.Execute(const Instance: IHadeVisited);
begin
  inherited Execute(Instance);
  if not Accept then
    Exit;

  save;
end;

{ THadeSQLAbs }

constructor THadeSQLAbs.Create(const AConnection: THadeConnection);
begin
  FAccessor := THadeDataAccessor.Create(AConnection);
  FMappingManager := GHadeMappingManager.Find(AConnection.ConnectionName);
end;

procedure THadeSQLAbs.Execute(const Instance: IHadeVisited);
begin
  fVisited := Instance as THadeVisited;
end;

destructor THadeSQLAbs.Destroy;
begin
  FreeAndNil(FAccessor);
  inherited Destroy;
end;

{ THadeSQLCreateAbs }

function THadeSQLCreate.Accept: boolean;
begin
  Result := not (FVisited is THadeObjectList);
end;

procedure THadeSQLCreate.save;
var
  MapClass: THadeMapClass;
  ACols: THadeMapSimpleValue;
begin
  try
    MapClass := FMappingManager.ClassByName(FVisited.ClassName);
    ACols := getWriteAbleSimpleMap(fVisited, MapClass);

    FAccessor.Insert(MapClass.Table.TableName, THadeObject(fVisited), ACols);
  finally
    FreeAndNil(ACols);
  end;
end;

initialization
  GHadeVisitorManager.RegisterVisitor('hadeSQLRead', THadeSQLRead);
  GHadeVisitorManager.RegisterVisitor('hadeSQLCreate', THadeSQLCreate);
  GHadeVisitorManager.RegisterVisitor('hadeSQLUpdate', THadeSQLUpdate);
  GHadeVisitorManager.RegisterVisitor('hadeSQLDelete', THadeSQLDelete);
  GHadeVisitorManager.RegisterVisitor('hadeSQLReadList', THadeSQLReadList);
  GHadeVisitorManager.RegisterVisitor('hadeSQLSaveList', THadeSQLSaveList);
end.

